package model;

/**
 * This class represents an auction.
 * An auction has an unique id, a description,
 * an owner, a end-date-time and the highest bidder.
 *
 * @version 1.0 vom 16.10.2012
 * @author Peter Tillian 1026312
 */
import java.rmi.RemoteException;
import java.sql.Timestamp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import model.event.AuctionEvent;
import model.event.BidEvent;
import model.event.Event;
import model.event.EventType;

import server.analytics.AnalyticsServer;
import server.billing.BillingServerSecure;

public class Auction {
	private static int idCounter = 1;
	private long id;
	private String description;
	private int duration;
	private User owner;
	private Timestamp startTime;
	private Timestamp endTime;
	private User highestBidder;
	private double highestBid;
	private List<User> allBidder;
	private boolean canceled;
	private AnalyticsServer analyticsServer = null;
	private BillingServerSecure billingServerSecure = null;
	private Timer timer;

	public Auction(String description, int duration, User owner,
			int secondsToEnd, AnalyticsServer analyticsServer,
			BillingServerSecure billingServerSecure) {
		synchronized (this.getClass()) {
			this.id = Auction.idCounter++;
		}

		this.description = description;
		this.duration = duration;
		this.owner = owner;
		this.startTime = new Timestamp(System.currentTimeMillis());
		this.endTime = new Timestamp(System.currentTimeMillis()
				+ (secondsToEnd * 1000));
		this.highestBid = 0;
		this.highestBidder = null;
		this.allBidder = Collections.synchronizedList(new ArrayList<User>());
		this.canceled = false;
		this.analyticsServer = analyticsServer;
		this.billingServerSecure = billingServerSecure;
		timer = new Timer();
		ObjectManagementPool.getInstance().addAuctionTimer(timer);
		timer.schedule(new AuctionTimerTask(this), this.duration * 1000);
	}

	public String toString() {
		if (this.highestBidder == null) {
			return this.id + ". '" + this.description + "' "
					+ this.owner.getUsername() + " " + this.endTime + " "
					+ this.highestBid + " none";
		} else {
			return this.id + ". '" + this.description + "' "
					+ this.owner.getUsername() + " " + this.endTime + " "
					+ this.highestBid + " " + this.highestBidder.getUsername();
		}
	}

	public long getId() {
		return this.id;
	}

	public double getHighestBid() {
		return this.highestBid;
	}

	public User getHighestBidder() {
		return this.highestBidder;
	}

	public String getDescription() {
		return this.description;
	}

	public User getOwner() {
		return this.owner;
	}

	public Timestamp getEndTime() {
		return this.endTime;
	}

	public void cancel() {
		this.canceled = true;
		ObjectManagementPool.getInstance().expireAuction(this);
	}

	public boolean isCanceled() {
		return this.canceled;
	}

	public synchronized boolean bid(double amount, User bidder) {
		if (amount <= this.highestBid) {
			return false;
		}

		if (this.highestBidder != null) {
			this.highestBidder.addNotification("You have been overbid on "
					+ this.description + ".");
			// send event to analyticsServer
			if (analyticsServer != null) {
				try {
					Event overbidAuction = new BidEvent(EventType.BID_OVERBID,
							this.highestBidder.getUsername(), this.id, amount);
					analyticsServer.processEvent(overbidAuction);
				} catch (RemoteException e) {
					System.err
							.println("Could not send overbid_event: AnalyticsServer shut down!");
				}
			}
		}
		this.highestBid = amount;
		this.highestBidder = bidder;
		if (!this.allBidder.contains(bidder))
			this.allBidder.add(bidder);

		// send event to analyticsServer
		if (analyticsServer != null) {
			try {
				Event bidAuction = new BidEvent(EventType.BID_PLACED,
						this.highestBidder.getUsername(), this.id, amount);
				analyticsServer.processEvent(bidAuction);
			} catch (RemoteException e) {
				System.err
						.println("Could not send bidplaced_event: AnalyticsServer shut down!");
			}
		}

		return true;
	}

	public long getRuntimeMillis() {
		return new Timestamp(System.currentTimeMillis()).getTime()
				- this.startTime.getTime();
	}

	private class AuctionTimerTask extends TimerTask {

		private Auction auction;

		public AuctionTimerTask(Auction auction) {
			this.auction = auction;
		}

		public void run() {
			ObjectManagementPool.getInstance().expireAuction(auction);
			String highestBidderName = "Nobody";
			if (auction.getHighestBidder() != null) {
				highestBidderName = auction.getHighestBidder().getUsername();
				auction.getHighestBidder().addNotification(
						"The auction '" + auction.getDescription()
								+ "' has ended. " + "You won with "
								+ auction.getHighestBid() + "!");
			}
			if (auction.getHighestBidder() != auction.getOwner()) {
				auction.getOwner().addNotification(
						"The auction '" + auction.getDescription()
								+ "' has ended. " + highestBidderName
								+ " won with " + auction.getHighestBid() + ".");
			}
			// create bill for auction
			try {
				if (billingServerSecure != null) {
					billingServerSecure.billAuction(owner.getUsername(), id,
							highestBid);
				} else {
					System.err.println("Could not bill the auction with id " + id
							+ ": BillingServer shut down!");
				}
			} catch (RemoteException e) {
				System.err.println("Could not bill the auction with id " + id
						+ ": BillingServer shut down!");
			}
			// send event to analyticsServer
			if (analyticsServer != null) {
				try {
					analyticsServer.processEvent(new AuctionEvent(
							EventType.AUCTION_ENDED, id));
				} catch (RemoteException e) {
					System.err
							.println("Could not send auction_ended event: AnalyticsServer shut down!");
				}
				try {
					if (highestBidder != null)
						analyticsServer.processEvent(new BidEvent(
								EventType.BID_WON, highestBidder.getUsername(),
								id, highestBid));
				} catch (RemoteException e) {
					System.err
							.println("Could not send bid_won event: AnalyticsServer shut down!");
				}
			}

			timer.cancel();
		}
	} // end of inner class AuctionTimerTask

} // end of class Auction